#include "image_buffer.hpp"

ImageBuffer::ImageBuffer(double metrics_window_duration) 
    : write_idx_(0), read_idx_(1), new_frame_ready_(false),
      metrics_(metrics_window_duration) {
}

void ImageBuffer::write(const cv::Mat& image) {
    std::lock_guard<std::mutex> write_lock(write_mutex_);
    
    auto now = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::duration<double>>
                   (now - metrics_.window_start_time).count();
    
    if (elapsed >= metrics_.window_duration_seconds) {
        metrics_.write_count_window = 0;
        metrics_.read_count_window = 0;
        metrics_.window_start_time = now;
    }
    
    image.copyTo(buffers_[write_idx_]);
    
    {
        std::lock_guard<std::mutex> swap_lock(swap_mutex_);
        swap_indices();
        new_frame_ready_.store(true);
    }
    
    metrics_.write_count_window++;
    metrics_.last_write_time = now;
    frame_cv_.notify_one();
}

bool ImageBuffer::read(cv::Mat& output, int timeout_ms) {
    std::unique_lock<std::mutex> read_lock(read_mutex_);
    
    bool frame_ready = frame_cv_.wait_for(read_lock, 
        std::chrono::milliseconds(timeout_ms),
        [this] { return new_frame_ready_.load(); }
    );
    
    if (frame_ready) {
        std::lock_guard<std::mutex> swap_lock(swap_mutex_);
        buffers_[read_idx_].copyTo(output);
        new_frame_ready_.store(false);
        
        metrics_.read_count_window++;
        metrics_.last_read_time = std::chrono::steady_clock::now();
        return true;
    }
    return false;
}

void ImageBuffer::swap_indices() {
    int temp = write_idx_.load();
    write_idx_.store(read_idx_.load());
    read_idx_.store(temp);
}

ImageBuffer::Metrics ImageBuffer::get_metrics() const {
    auto now = std::chrono::steady_clock::now();
    auto elapsed = std::chrono::duration_cast<std::chrono::duration<double>>
                   (now - metrics_.window_start_time).count();
    
    double write_fps = metrics_.write_count_window / 
                      std::min(elapsed, metrics_.window_duration_seconds);
    double read_fps = metrics_.read_count_window / 
                      std::min(elapsed, metrics_.window_duration_seconds);
    
    Metrics result;
    result.write_fps = write_fps;
    result.read_fps = read_fps;
    result.dropped_frames = metrics_.dropped_frames;
    return result;
} 